package com.example.demo.config;

import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Primary;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.util.ObjectUtils;
import org.springframework.util.concurrent.ListenableFuture;

import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.lang.management.OperatingSystemMXBean;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * @author yzd
 * @author yzd
 * @Description: 显示线程池信息
 * <p>
 * 显示线程执行情况：任务总数、已完成数、活跃线程数，队列大小信息
 * </p>
 * @date 2021年7月22日09:37:41
 **/
@Data
@EqualsAndHashCode(callSuper = true)
@Slf4j
@Primary
public class VisibleThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {

    private static final long serialVersionUID = 8782155982988308307L;

    private void showThreadPoolInfo(String prefix) {
        ThreadPoolExecutor threadPoolExecutor = getThreadPoolExecutor();

        if (ObjectUtils.isEmpty(threadPoolExecutor)) {
            return;
        }

        log.info("{}, {},taskCount [{}], completedTaskCount [{}], activeCount [{}], queueSize [{}]",
                this.getThreadNamePrefix(),
                prefix,
                threadPoolExecutor.getTaskCount(),
                threadPoolExecutor.getCompletedTaskCount(),
                threadPoolExecutor.getActiveCount(),
                threadPoolExecutor.getQueue().size());

        this.showMemoryInfo();
    }

    @Override
    public void execute(Runnable task) {
        showThreadPoolInfo("1. do execute");
        super.execute(task);
    }

    @Override
    public void execute(Runnable task, long startTimeout) {
        showThreadPoolInfo("2. do execute timeout");
        super.execute(task, startTimeout);
    }

    @Override
    public Future<?> submit(Runnable task) {
        showThreadPoolInfo("1. do submit");
        return super.submit(task);
    }

    @Override
    public <T> Future<T> submit(Callable<T> task) {
        showThreadPoolInfo("2. do submit");
        return super.submit(task);
    }

    @Override
    public ListenableFuture<?> submitListenable(Runnable task) {
        showThreadPoolInfo("1. do submitListenable");
        return super.submitListenable(task);
    }

    @Override
    public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
        showThreadPoolInfo("2. do submitListenable");
        return super.submitListenable(task);
    }

    /**
     * Java 虚拟机的内存系统
     * <p>
     * init约等于xms的值，max约等于xmx的值
     * used是已经被使用的内存大小，committed是当前可使用的内存大小（包括已使用的）
     * committed >= used。committed不足时jvm向系统申请，若超过max则发生OutOfMemoryError错误
     * 机器内存小于192M,堆内存最大为二分之一
     * 机器内存大于等于1024M，堆内存最大为四分之一
     * JVM初始分配的内存由-Xms指定，默认是物理内存的1/64；
     * JVM最大分配的内存由-Xmx指定，默认是物理内存的1/4。
     * 默认空余堆内存小于40%时，JVM就会增大堆直到-Xmx的最大限制；
     * 空余堆内存大于70%时，JVM会减少堆直到-Xms的最小限制。
     * 因此服务器一般设置-Xms、-Xmx相等以避免在每次GC后调整堆的大小。
     * </p>
     */
    private void showMemoryInfo() {
        MemoryMXBean mem = ManagementFactory.getMemoryMXBean();
        MemoryUsage heap = mem.getHeapMemoryUsage();
        log.info("Heap committed:" + heap.getCommitted() / 1024 / 1024
                + "MB, init:" + heap.getInit() / 1024 / 1024
                + "MB, max:" + heap.getMax() / 1024 / 1024
                + "MB, used:" + heap.getUsed() / 1024 / 1024 + "MB");
    }

    /**
     * Java 虚拟机在其上运行的操作系统
     */
    private void showSystem() {
        OperatingSystemMXBean op = ManagementFactory.getOperatingSystemMXBean();
        log.info("Architecture: " + op.getArch() + "," +
                "Processors: " + op.getAvailableProcessors() + "," +
                "System name: " + op.getName() + "," +
                "System version: " + op.getVersion() + "," +
                "Last minute load: " + op.getSystemLoadAverage());
    }

}
